package oss

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"time"

	"clouds.lgb24kcs.cn/global"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/credentials"
	"github.com/aws/aws-sdk-go-v2/service/s3"
)

type OssStorage interface {
	NewClient()
	CreateBucket()
	BucketExists()
	DeleteBucket()
	CreateCatalogue()
	CatalogueExists()
	RenameCatalogue()
	CatalogueDelete()
	ObjectExists()
	DelObject()
	UploadFile()
	ObjectDownload()
	CheckFileObject()
	ShareFile()
}

type OSS struct {
}

// 初始化客户端
func (o *OSS) NewClient(access_key, secret_key, end_point string) *s3.Client {
	customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
		return aws.Endpoint{
			URL: end_point,
		}, nil
	})
	cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithEndpointResolverWithOptions(customResolver),
		config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
			access_key, secret_key, "",
		)))
	if err != nil {
		panic(err)
	}

	svc := s3.NewFromConfig(cfg, func(options *s3.Options) {
		options.UsePathStyle = true // 这里没有将桶名放在域名中

	})
	return svc

}

// 创建桶
func (o *OSS) CreateBucket(svc *s3.Client, BucketName string) error {
	fmt.Println("bucket_name: ", BucketName)
	ctx := context.TODO()
	createBucketInput := &s3.CreateBucketInput{
		Bucket: &BucketName,
	}
	_, err := svc.CreateBucket(ctx, createBucketInput)
	if err != nil {
		return err
	} else {

		// 设置存储桶策略为publice

		// 创建存储桶策略 JSON，使存储桶对所有人具有读取权限
		bucketPolicy := `{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": "*",
                "Action": "s3:GetObject",
                "Resource": "arn:aws:s3:::` + BucketName + `/*"
            }
        ]
    }`
		_, err = svc.PutBucketPolicy(context.TODO(), &s3.PutBucketPolicyInput{
			Bucket: &BucketName,
			Policy: &bucketPolicy,
		})
		if err != nil {
			return err
		}
		return nil
	}

}

/*
检查桶是否存在
前端加载存储空间时，就建议桶是否存在
*/
func (o *OSS) BucketExists(svc *s3.Client, bucketName string) error {
	_, err := svc.HeadBucket(context.TODO(), &s3.HeadBucketInput{
		Bucket: aws.String(bucketName),
	})
	if err != nil {
		return err
	}
	return nil
}

// 删除桶
func (o *OSS) DeleteBucket(svc *s3.Client, BucketName string) error {
	/*
		递归删除桶下的所有数据
		1.先获取一下当前桶中是否存在子目录或文件
		2.如果有子数据，将子数据删除后再删除该桶
	*/

	err := o.BucketExists(svc, BucketName)
	if err != nil {
		return err
	} else {
		// 列出桶中的对象
		objects, err := svc.ListObjects(context.TODO(), &s3.ListObjectsInput{Bucket: aws.String(BucketName)})
		if err != nil {
			return err
		} else {
			// 判断bucket下是否存在文件对象
			if len(objects.Contents) == 0 {
				// 如果没有直接删除
				_, err = svc.DeleteBucket(context.TODO(), &s3.DeleteBucketInput{Bucket: aws.String(BucketName)})
				if err != nil {
					return err
				}
				return nil
			} else {
				// 如果存在文件对象，则先删除文件对象后再删除bucke，没有目录概念
				for _, item := range objects.Contents {
					_, err = svc.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
						Bucket: aws.String(BucketName),
						Key:    aws.String(*item.Key),
					})
					if err != nil {
						return err
					}
				}
				_, err = svc.DeleteBucket(context.TODO(), &s3.DeleteBucketInput{Bucket: aws.String(BucketName)})
				if err != nil {
					return err
				}
				return nil
			}
		}
	}

}

/*创建目录*/
func (o *OSS) CreateCatalogue(svc *s3.Client, BucketName string, Catalogue string) error {
	if ok := o.CatalogueExists(svc, BucketName, Catalogue); ok {
		return errors.New("该目录已存在")
	} else {
		// 创建目录，如果要创建嵌套目录，请在目录路径中包含完整的路径，例如 directoryPath := "folder/subfolder/"
		directoryPath := Catalogue + "/" // 一定要加最后一根斜杠否则创建的是以字节数为0的文件
		input := &s3.PutObjectInput{
			Bucket: &BucketName,
			Key:    &directoryPath,
		}
		_, err := svc.PutObject(context.TODO(), input)
		if err != nil {
			return err
		} else {
			return nil
		}

	}

}

/*检查目录对象是否存在*/
func (o *OSS) CatalogueExists(svc *s3.Client, BucketName, CatalogueName string) bool {
	_, err := svc.GetObject(context.TODO(), &s3.GetObjectInput{
		Bucket: aws.String(BucketName),
		Key:    aws.String(CatalogueName),
	})
	if err != nil {
		return false
	}

	return true
}

/*目录对象详情信息*/
func (o *OSS) CatalogueDetails(svc *s3.Client, BucketName, CatalogueName string) error {
	if exists := o.CatalogueExists(svc, BucketName, CatalogueName); exists {
		return nil
	}
	return errors.New("目录不存在")
}

/*删除目录，支持多目录删除*/
func (o *OSS) CatalogueDelete(svc *s3.Client, BucketName string, Catalogue []string) error {
	for _, Catalogue := range Catalogue {
		// 查看当前目录下是否有文件对象
		result, err := svc.ListObjects(context.TODO(), &s3.ListObjectsInput{Bucket: aws.String(BucketName), Prefix: aws.String(Catalogue + "/")})
		if err != nil {
		} else {
			for _, obj := range result.Contents {
				// 判断目录下是否存在文件，如果有就直接删除
				_, err := svc.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
					Bucket: aws.String(BucketName),
					Key:    aws.String(*obj.Key),
				})
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}

/*
重命名目录：不要调这个借口，这个借口存在bug没有修复
判断目录下是否存在文件数据，如果有将文件复制到新命名的目录中，然后在删除源目录
*/
func (o *OSS) RenameCatalogue(svc *s3.Client, sourceBucket, sourceCatalogueKey, destinationBucket, destinationCatalogueKey string) {
	// 定义源和目标对象的桶和键
	_, err := svc.CopyObject(context.TODO(), &s3.CopyObjectInput{
		Bucket:     &destinationBucket,
		CopySource: aws.String(sourceBucket + "/" + sourceCatalogueKey),
		Key:        aws.String(destinationCatalogueKey + "/"),
	})
	if err != nil {
		fmt.Println(err)
	} else {
	}
	// 删除原始对象
	_, err = svc.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
		Bucket: &sourceBucket,
		Key:    aws.String(sourceBucket + "/" + sourceCatalogueKey),
	})
	if err != nil {
		log.Fatal(err)
	}
}

/*移动目录*/

/*检查对象是否存在*/
func (o *OSS) ObjectExists(svc *s3.Client, BucketName, ObjectName string) bool {
	// catalogueName+"/"+fileName
	_, err := svc.GetObject(context.TODO(), &s3.GetObjectInput{
		Bucket: aws.String(BucketName),
		Key:    aws.String(ObjectName),
	})
	if err != nil {
		global.APP.Log.Error(err.Error())
		return false
	}

	return true
}

/*删除单个文件对象*/
func (o *OSS) DelObject(svc *s3.Client, BucketName string, objectKeys string) error {
	_, err := svc.DeleteObject(context.TODO(), &s3.DeleteObjectInput{Bucket: aws.String(BucketName), Key: aws.String(objectKeys)})
	if err != nil {
		global.APP.Log.Error(err.Error())
		return err
	} else {
		return nil
	}
}

/*上传文件*/
// objectKey目录名称
func (o *OSS) UploadFile(svc *s3.Client, bucketName, catalogueName, file_dir, fileName string) error {
	open, err := os.Open(file_dir)
	if err != nil {
		return err
	} else {
		//bucketName + "/" + 学习资料 + "/" + filename
		directoryPath := catalogueName + "/" + fileName // 文件目录
		_, err = svc.PutObject(context.Background(), &s3.PutObjectInput{Bucket: aws.String(bucketName), Key: aws.String(directoryPath), Body: open})
		if err != nil {
			return err
		}
	}
	defer open.Close()
	return nil
}

/*下载对象数据*/
func (o *OSS) ObjectDownload(svc *s3.Client, bucketName string, objectKey string) string {
	dir, _ := os.Getwd()
	fileDir := dir + global.APP.Configuration.Server.Upload + "/" + strings.Split(objectKey, "/")[len(strings.Split(objectKey, "/"))-1]
	file, err := os.OpenFile(fileDir, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		global.APP.Log.Error(err.Error())
		return ""
	}
	defer file.Close()
	object, err := svc.GetObject(context.TODO(), &s3.GetObjectInput{
		Bucket: aws.String(bucketName),
		Key:    aws.String(objectKey),
	})
	if err != nil {
		global.APP.Log.Error(err.Error())
		return ""
	}
	defer object.Body.Close()
	all, err := io.ReadAll(object.Body)
	if err != nil {
		global.APP.Log.Error(err.Error())
		return ""
	}
	_, err = file.Write(all)
	if err != nil {
		global.APP.Log.Error(err.Error())
		return ""
	} else {
		return fileDir
	}
}

/*判断某个目录下的文件是否存在*/
func (o *OSS) CheckFileObject(svc *s3.Client, bucketName, ObjectName string) bool {
	_, err := svc.HeadObject(context.TODO(), &s3.HeadObjectInput{Bucket: aws.String(bucketName), Key: aws.String(ObjectName)})
	if err != nil {
		global.APP.Log.Error(err.Error())
		return false
	} else {
		return true
	}
}

/*文件分享*/
func (o *OSS) ShareFile(svc *s3.Client, bucketName, ObjectName string, Expiration time.Duration) (string, error) {
	// 创建预签名客户端
	client := s3.NewPresignClient(svc)
	// 生成预签名URL
	presignedURL, err := client.PresignGetObject(context.TODO(), &s3.GetObjectInput{
		Bucket: aws.String(bucketName),
		Key:    aws.String(ObjectName),
	}, func(options *s3.PresignOptions) {
		options.Expires = Expiration
	})
	if err != nil {
		return "", err
	} else {
		return presignedURL.URL, nil
	}
}
